<?php

defined('ROOT') or die('No direct script access.');
//include_once("cdbconnection.php");
abstract class CModel extends CBaseObject
{
    private $db_connection;
    private $fetch_type;
    private $save_only_not_nulls;

    /**
     * @param string $db идентификатор соединения с БД
     * @param CDBConnection $db объект соединения
     */
    function __construct($db = "default")
    {
        if (is_string($db)) {
            $this->db_connection = new CDBConnection($db);
        } elseif (is_object($db)) {
            $this->db_connection = $db;
        } else {
            $this->db_connection = new CDBConnection("default");
        }
        // $this->db_connection = CDBConnection::getInstance();
        self::set_fetch_type();
    }

    public function set_fetch_type($param = "OBJ")
    {
        $this->fetch_type = $param;
    }

    public function get_fetch_type()
    {
        if ($this->fetch_type == "ASSOC") return PDO::FETCH_ASSOC;
        if ($this->fetch_type == "BOTH") return PDO::FETCH_BOTH;
        if ($this->fetch_type == "BOUND") return PDO::FETCH_BOUND;
        if ($this->fetch_type == "CLASS") return PDO::FETCH_CLASS;
        if ($this->fetch_type == "INTO") return PDO::FETCH_INTO;
        if ($this->fetch_type == "LAZY") return PDO::FETCH_LAZY;
        if ($this->fetch_type == "NUM") return PDO::FETCH_NUM;
        if ($this->fetch_type == "OBJ") return PDO::FETCH_OBJ;
    }

    /**
     * @return CDBConnection соединение с БД модели
     */
    public function db()
    {
        return $this->db_connection;
    }

    /**
     * Выполнение произвольного запроса с параметрами или без
     * @param string $prmSqlText строка запроса
     * @param array $prmParams массив параметров  (не обязателен)
     * @return
     */
    public function execute_sql($prmSqlText, $prmParams = 0)
    {
        $sth = $this->db()->handle()->prepare(self::parse_dbprefix($prmSqlText));
        if ($prmParams == 0) {
            $sth->execute();
        } else {
            $sth->execute($prmParams);
        }
        return $sth;
    }

    /**
     * Присваивание реального имени таблицы (замена псевдопрефикса на реальный)
     * @param string $param строка запроса до замены
     * @return string строка запроса после замены
     */
    private function parse_dbprefix($param)
    {
        return str_replace("##_", $this->db()->get_prefix(), $param);
    }

    /**
     * Получение набора записей произвольным запросом
     * @param string $prmSqlText строка запроса
     * @param array $prmParams массив параметров (не обязателен)
     * @return type
     */
    public function fetch_all_sql($prmSqlText, $prmParams = 0, $prm_class = '')
    {
        $sth = self::execute_sql($prmSqlText, $prmParams);
        if ($prm_class != '') {
            return $sth->fetchAll(PDO::FETCH_CLASS, $prm_class);
        } else {
            return $sth->fetchAll(self::get_fetch_type());
        }
    }

    /**
     * Получение одной записи произвольным запросом
     * @param string $prmSqlText строка запроса
     * @param array $prmParams массив параметров (не обязателен)
     * @return type
     */
    public function fetch_sql($prmSqlText, $prmParams = 0, $prm_class = "")
    {
        //        self::debug_msg($prmSqlText);
        //        print_r($prmParams);
        //        self::debug_msg($prm_class);
        $sth = self::execute_sql($prmSqlText, $prmParams);
        if ($prm_class != '') {
            //            return $sth->fetch(PDO::FETCH_CLASS, $prm_class);
            return $sth->fetchObject($prm_class);
        } else {
            return $sth->fetch(self::get_fetch_type());
        }
    }

    /*
     * Получаем список для выбора на форме
     * @param Массив параметров
     * table - Имя таблицы выборки
     */
    public function for_select($param)
    {
        $table = $param['table'];
        $sql   = "select id,title from " . $table;
        return self::fetch_all($sql);
    }

    /**
     * Вставка записи в таблицу
     * @param string $prmTable идентификатор таблицы
     * @param array $prmData массив данных (ключ соответствует названию колонки)
     * @return mixed ID добавленной записи
     */
    public function insert_array($prmTable, $prmData)
    {
        $fields     = "";
        $fieldsVals = "";
        foreach (array_keys($prmData) as $key) {
            $fields .= (($fields == "") ? '' : ',') . $key;
            $fieldsVals .= (($fieldsVals == "") ? ':' : ',:') . $key;
        }
        $query = "insert into " . self::parse_dbprefix($prmTable) . " ({$fields}) values ({$fieldsVals})";
        self::execute_sql($query, $prmData);
        return $this->db()->handle()->lastInsertId();
    }

    /**
     *
     * @param string $sqltext текст запроса выборки
     * @return array
     */
    public function select($sqltext)
    {
        return $this->db()->handle()->query(self::parse_dbprefix($sqltext));
    }

    /**
     * Получить строку из таблицы
     * @param  [type] $table      Название таблицы
     * @param  [type] $fields     Список полей
     * @param  [type] $conditions либо ID записи, либо массив условий
     * @return [type]             результат выборки, либо FALSE
     */
    public function fetch($table, $fields, $conditions)
    {
        $sql = "select $fields from $table";
        if (is_array($conditions)) {
            $fields = "";
            foreach (array_keys($conditions) as $key) {
                $fields .= (($fields == "") ? "{$key} = :{$key}" : " and {$key} = :{$key}");
            }
            $sql .= ' where ' . $fields;
        } else {
            $sql        = "select $fields from $table where id = :id";
            $conditions = array('id' => $conditions);
        }
        // self::log($conditions,$sql);
        return self::fetch_sql($sql, $conditions);
    }

    public function fetch_all($table, $columns = '*', $conditions = array(), $result_type = '')
    {
        $sql = "select $columns from $table";
        if (count($conditions) > 0) {
            $fields = "";
            foreach (array_keys($conditions) as $key) {
                $fields .= (($fields == "") ? "{$key} = :{$key}" : " and {$key} = :{$key}");
            }
            $sql .= ' where ' . $fields;

        }
        return self::fetch_all_sql($sql, $conditions, $result_type);
    }

//    public function fetch_all_with_fields($table, $fields, $condition)
//    {
//        $sql = "select $fields from $table where $condition";
//        return self::fetch($sql);
//    }

    /**
     * Получение строки из указанной табилцы по ID
     * @param string $prmTable идентификатор таблицы
     * @param variant $prmID искомый ID
     * @return array|object
     */
    public function row_by_id($prmTable, $prmID)
    {
        $query = "select * from " . self::parse_dbprefix($prmTable) . " where id=:id";
        return self::fetch_sql($query, array(':id' => $prmID));
    }

    /**
     * Обновление записи в БД по ID
     * @param string $prmTable идентификатор таблицы
     * @param array $prmData  массив данных, ключ соответствует названию колонки.
     */
    public function update_by_id($prmTable, $prmData)
    {
        $fieldsVals = "";
        foreach (array_keys($prmData) as $key) {
            if ($key != "id") {
                $fieldsVals .= (($fieldsVals == "") ? "{$key} = :{$key}" : " , {$key} = :{$key}");
            }
        }
        $query = "update {$prmTable} set {$fieldsVals} where id = :id";
//        error_log($query);
        $this->execute_sql(self::parse_dbprefix($query), $prmData);
        return $prmData['id'];
    }

    /*
    * Сохраняем данные в БД, определяется необходимый метод на основе поля ID
    * @param string $prm_table идентификатор таблицы
    * @param array $prm_data  массив данных, ключ соответствует названию колонки.
    */
    public function save_to($prm_table, $prm_data)
    {
        $act_update = false;
        $data       = (array)$prm_data;
        //        print_r($data);
        //        self::debug_msg($prm_table);
        if (isset($data['id'])) {
            if ($data['id'] > 0) {
                $act_update = true;
            }
        }
        if ($act_update) {
            return self::update_by_id($prm_table, $data);
        } else {
            return self::insert_array($prm_table, $data);
        }
    }

    //TODO: Сделать поддержку нескольких SQL серверов
    public function upsert($table, $data)
    {
        $fields        = "";
        $fieldsVals    = "";
        $fields_update = "";
        foreach (array_keys($data) as $key) {
            $fields .= (($fields == "") ? '' : ',') . $key;
            $fieldsVals .= (($fieldsVals == "") ? ':' : ',:') . $key;
            $fields_update .= (($fields_update == "") ? "{$key} = :{$key}" : " , {$key} = :{$key}");
        }
        $query = "insert into $table ({$fields}) values ({$fieldsVals}) ON DUPLICATE KEY UPDATE $fields_update";
        self::execute_sql($query, $data);
        return $this->db()->handle()->lastInsertId();
    }

}